package org.hbgl.geoentity.admin.aspect;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.hbgl.geoentity.core.config.SystemProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * 代码功能：接口调用日志
 * 开发日期：2022/4/13
 * 作者：蒋海仑
 * 联系方式：
 * 版本更迭说明：
 * 版权信息：河北广联信息技术有限公司
 */
@Component
@Aspect
public class RequestLogAspect {
    private final Log logger = LogFactory.getLog(RequestLogAspect.class);

    @Autowired
    private SystemProperties properties;

    private final String[] ignoreApi = {
            "/public/address",
            "/public/unitType",
            "/public/trade",
            "/public/geographyType",
            "/public/storage/fetch"
    };

    @Pointcut("execution(* org.hbgl.geoentity.admin.web..*(..))")
    public void requestServer() {
    }

    @Around("requestServer()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result = joinPoint.proceed();
        if (!properties.isOutputRequestLog()) return result;

        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        RequestInfo requestInfo = new RequestInfo();
        requestInfo.setIp(request.getRemoteAddr());
        requestInfo.setUrl(request.getRequestURL().toString());
        for (String api : ignoreApi) {
            if (requestInfo.getUrl().contains(api)) {
                return result;
            }
        }
        requestInfo.setHttpMethod(request.getMethod());
        Signature signature = joinPoint.getSignature();
        requestInfo.setClassMethod(String.format("%s.%s", signature.getDeclaringTypeName(), signature.getName()));
        requestInfo.setArgs(BaseAspect.getRequestParamsByProceedingJoinPoint(joinPoint));
        requestInfo.setResult(result);
        requestInfo.setTimeCost(System.currentTimeMillis() - start);
        logger.info(JSON.toJSONString(requestInfo, SerializerFeature.PrettyFormat));
        return result;
    }

    @AfterThrowing(pointcut = "requestServer()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
        requestErrorInfo.setIp(request.getRemoteAddr());
        requestErrorInfo.setUrl(request.getRequestURL().toString());
        requestErrorInfo.setHttpMethod(request.getMethod());
        Signature signature = joinPoint.getSignature();
        requestErrorInfo.setClassMethod(String.format("%s.%s", signature.getDeclaringTypeName(), signature.getName()));
        requestErrorInfo.setArgs(BaseAspect.getRequestParamsByJoinPoint(joinPoint));
        requestErrorInfo.setException(e);
        logger.info(JSON.toJSONString(requestErrorInfo, SerializerFeature.PrettyFormat));
    }

    @Data
    public static class BaseInfo {
        private String ip;
        private String url;
        private String httpMethod;
        private String classMethod;
        private Object args;
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    public static class RequestInfo extends BaseInfo {
        private Object result;
        private Long timeCost;
    }

    @EqualsAndHashCode(callSuper = true)
    @Data
    public static class RequestErrorInfo extends BaseInfo {
        private RuntimeException exception;
    }
}
